﻿using System;
using System.Collections.Generic;
using System.Text;
using Pixysoft.Framework.Configurations;
using System.Reflection;
using System.IO;
using Pixysoft.Framework.Verifications;

namespace Pixysoft.Framework.Remoting.Controller
{
    class ReflectionController
    {
        private const string default_attribute_assembly = "assembly";
        private const string default_attribute_type = "type";
        private const string default_attribute_method = "method";
        private const string default_attribute_parametercount = "parametercount";
        private const string default_attribute_return = "return";

        private const string default_attribute_parameter = "parameter";
        //private const string default_attribute_parametertype = "parametertype";

        private static volatile ReflectionController instance;

        private static object syncRoot = new Object();

        public static ReflectionController Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                        {
                            instance = new ReflectionController();
                        }
                    }
                }
                return instance;
            }
        }

        public IXmlNode GetMethodDeclaration(MethodInfo method, params object[] paras)
        {
            IXmlNode node = XmlManager.CreateNode(default_attribute_method);

            RemoteAttribute remote = Pixysoft.Reflection.ReflectHelper.Instance.GetCustomerAttribute<RemoteAttribute>(method.DeclaringType);

            if (remote == null)
                throw Exceptions.ManualVerificationFailedException("missing remote definition");

            if (!Verification.Verifies(remote.AssemblyName, remote.TypeName).IsNotNullOrEmpty().Result())
                throw Exceptions.ManualVerificationFailedException("missing assembly name, type name.");

            LoggerHelper.Debug("begin create method delcaration. methodname = {0}", method.Name);

            node.Properties.Add(default_attribute_assembly, remote.AssemblyName);
            node.Properties.Add(default_attribute_type, remote.TypeName);
            node.Properties.Add(default_attribute_method, method.Name);
            node.Properties.Add(default_attribute_parametercount, method.GetParameters().Length.ToString());

            int counter = 0;
            foreach (ParameterInfo parameter in method.GetParameters())
            {
                IXmlNode paranode = node.Nodes.Add(default_attribute_parameter);
                paranode.Properties.Add(default_attribute_type, parameter.ParameterType.Name);
                paranode.Properties.Add(default_attribute_parameter, parameter.Name);

                object paravalue = paras[counter++];

                if (paravalue == null)
                    continue;

                paranode.Text = XmlManager.Serialize(paravalue).Serialize();
            }

            IXmlNode returnnode = node.Nodes.Add(default_attribute_return);
            returnnode.Properties.Add(default_attribute_type, method.ReturnType.Name);
            return node;
        }


        /// <summary>
        /// 取得反序列化为对象的结果
        /// </summary>
        /// <param name="result"></param>
        /// <returns></returns>
        public object GetDeserializeRemoteCallResult(Type type, string content)
        {
            if (type.Equals(typeof(void)))
                return null;

            return XmlManager.PojoDeserialize(type, content);
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public string GetSerializeRemoteCallResult(string content)
        {
            IXmlNode node = XmlManager.Deserialize(content);

            Type type = GetType(node);

            MethodInfo method = GetMethod(type, node);

            object callvalue = GetMethodCallResult(type, node);

            //对于void，返回null

            if (callvalue == null)
                return null;

            return XmlManager.Serialize(callvalue).Serialize();
        }

        /// <summary>
        /// 调用方法
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        internal object GetMethodCallResult(IXmlNode node)
        {
            Type type = GetType(node);

            return GetMethodCallResult(type, node);
        }

        private object GetMethodCallResult(Type type, IXmlNode node)
        {
            MethodInfo method = GetMethod(type, node);

            ConstructorInfo constructorInfo =
                type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
                null, new Type[] { }, null);

            object remoteObject = null;

            if (constructorInfo == null)
            {
                throw Exceptions.UnexpectedResultException("create type constructor info fail. typename = " + type.Name);
            }

            remoteObject = constructorInfo.Invoke(new object[] { });

            return method.Invoke(remoteObject, GetParameters(method, node));
        }

        private Type GetType(IXmlNode node)
        {
            string assemblyName = node.Properties[default_attribute_assembly];

            string typeName = node.Properties[default_attribute_type];

            //2010-10-06 增加了映射

            RemotingAssemblyMapping mapping = null;

            mapping = SettingHelper.Mappings.SearchMapping(assemblyName, typeName);

            if (mapping != null)
            {
                assemblyName = mapping.MappingAssemblyName;

                typeName = mapping.MappingTypeName;
            }
            else
            {
                mapping = SettingHelper.Mappings.SearchMapping(assemblyName);

                if (mapping != null)
                {
                    assemblyName = mapping.MappingAssemblyName;

                    typeName = typeName.Replace(mapping.AssemblyName, mapping.MappingAssemblyName);
                }
            }

            LoggerHelper.Debug("try to load type, assemblyname = {0}, typename = {1}", assemblyName, typeName);

            Type type = Type.GetType(typeName);

            if (type == null)
            {
                Assembly assembly = GetAssembly(assemblyName);

                type = assembly.GetType(typeName);
            }

            if (type == null)
                throw Exceptions.UnexpectedResultException("load type fail. typename = " + typeName);

            return type;
        }

        private Assembly GetAssembly(string assemblyName)
        {
            LoggerHelper.Debug("try load assembly. assemblyname = {0}", assemblyName);

            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                if (string.Equals(
                    Pixysoft.Tools.AssemblyHelper.Instance.GetAssemblyName(assembly), assemblyName))
                    return assembly;
            }

            Assembly loadassembly = Pixysoft.Tools.AssemblyHelper.Instance.AssemblyLoadFrom(assemblyName + ".dll");

            if (loadassembly == null)
                throw Exceptions.UnexpectedResultException("load assembly failed. assembly name = " + assemblyName);

            return loadassembly;
        }

        private MethodInfo GetMethod(Type type, IXmlNode node)
        {
            string methodname = node.Properties[default_attribute_method];

            int parametercount = node.Properties.Get<int>(default_attribute_parametercount);

            LoggerHelper.Debug("try to get method, methodname = " + methodname);

            foreach (MethodInfo method in type.GetMethods())
            {
                if (!string.Equals(method.Name, methodname))
                    continue;

                ParameterInfo[] parameters = method.GetParameters();

                if (parameters.Length != parametercount)
                    continue;

                bool founded = true;

                for (int i = 0; i < parametercount; i++)
                {
                    IXmlNode parameternode = node.Nodes[default_attribute_parameter][i];

                    ParameterInfo parameter = parameters[i];

                    string paratypename = parameternode.Properties[default_attribute_type];

                    if (string.Equals(parameter.ParameterType.Name, paratypename))
                        continue;

                    founded = false;
                }

                if (!founded)
                    continue;

                return method;
            }

            throw Exceptions.UnexpectedResultException("load method fail. methodname = " + methodname);
        }

        private object[] GetParameters(MethodInfo method, IXmlNode node)
        {
            List<object> list = new List<object>();

            if (node.Properties.Get<int>(default_attribute_parametercount) != method.GetParameters().Length)
                throw Exceptions.ManualVerificationFailedException("count of parameter in request do not match the target.");

            int counter = 0;

            foreach (ParameterInfo parameter in method.GetParameters())
            {
                IXmlNode subnode = node.Nodes[default_attribute_parameter][counter++];

                string content = subnode.Text;

                list.Add(XmlManager.PojoDeserialize(parameter.ParameterType, content));

            }

            return list.ToArray();
        }


        //private void test002()
        //{
        //    IRemotingValue para = Pixysoft.Tools.PojoHelper.GetPojo<IRemotingValue>();
        //    para.Value = "hello";

        //    Type type = typeof(IRemotingHelloworld);
        //    MethodInfo method = type.GetMethods()[1];

        //    byte[] img = File.ReadAllBytes(@"E:\Jane Seymour 02.jpg");

        //    IXmlNode methodnode = GetMethodDeclaration(method, img);
        //    string methodstring = methodnode.Serialize(true);

        //    Type rtype = GetType(methodnode);
        //    MethodInfo rmethod = GetMethod(rtype, methodnode);
        //    object[] rparas = GetParameters(rmethod, methodnode);
        //    object rvalue = GetMethodCallResult(methodnode);

        //    string rsvalue = GetSerializeRemoteCallResult(methodnode);
        //    Console.WriteLine(rsvalue);
        //    rvalue = GetDeserializeRemoteCallResult(rmethod.ReturnType, rsvalue);
        //}
    }
}
